'user strict'

const Promise = require('bluebird');
const _ = require('lodash');
const request = Promise.promisify(require('request'));
const fs = require('fs');
const util = require('./util');

const prefix = 'https://api.weixin.qq.com/cgi-bin/';

// 接口api
let api = {
    // access_token获取：token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    accessToken: prefix + 'token?grant_type=client_credential', // 获取access_token
    // 新增临时素材：media/upload?access_token=ACCESS_TOKEN&type=TYPE
    upload: prefix + 'media/upload?',
    // 获取临时素材：media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
    fetch: prefix + 'media/get?',
    // 永久素材
    permanent: {
        // 新增素材：material/add_material?access_token=ACCESS_TOKEN&type=TYPE
        upload: prefix + 'material/add_material?',
        // 新增永久图文：material/add_news?access_token=ACCESS_TOKEN
        uploadNews: prefix + 'material/add_news?',
        // 新增图文内图片url：media/uploadimg?access_token=ACCESS_TOKEN
        uploadNewsPic: prefix + 'media/uploadimg?',
        // 获取永久素材：material/get_material?access_token=ACCESS_TOKEN
        fetch: prefix + 'material/get_material?',
        // 删除永久素材：material/del_material?access_token=ACCESS_TOKEN
        del: prefix + 'material/del_material?',
        // 修改永久素材：material/update_news?access_token=ACCESS_TOKEN
        update: prefix + 'material/update_news?',
        // 获取素材总数：material/get_materialcount?access_token=ACCESS_TOKEN
        count: prefix + 'material/get_materialcount?',
        // 获取素材列表：material/batchget_material?access_token=ACCESS_TOKEN
        batch: prefix + 'material/batchget_material?'
    },
    // 标签
    tag: {
        // 创建标签：tags/create?access_token=ACCESS_TOKEN
        create: prefix + 'tags/create?',
        // 获取已创建标签：tags/get?access_token=ACCESS_TOKEN
        fetch: prefix + 'tags/get?',
        // 编辑标签：tags/update?access_token=ACCESS_TOKEN
        update: prefix + 'tags/update?',
        // 删除标签：tags/delete?access_token=ACCESS_TOKEN
        del: prefix + 'tags/delete?',
        // 获取标签下粉丝列表：user/tag/get?access_token=ACCESS_TOKEN
        getUser: prefix + 'user/tag/get?',
        // 批量打标签：tags/members/batchtagging?access_token=ACCESS_TOKEN
        batchTag: prefix + 'tags/members/batchtagging?',
        // 批量取消标签：tags/members/batchuntagging?access_token=ACCESS_TOKEN
        batchUnTag: prefix + 'tags/members/batchuntagging?',
        // 获取用户的所有标签：tags/getidlist?access_token=ACCESS_TOKEN
        getTags: prefix + 'tags/getidlist?'
    },
    // 用户管理
    user: {
        // 获取用户基本信息：user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
        fetch: prefix + 'user/info?',
        // 批量获取用户信息：user/info/batchget?access_token=ACCESS_TOKEN
        batchFetch: prefix + 'user/info/batchget?',
        // 获取用户列表:user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
        list: prefix + 'user/get?'
    },
    mass: {
        // 群发：message/mass/sendall?access_token=ACCESS_TOKEN
        tag: `${prefix}message/mass/sendall?`
    },
    // 自定义菜单
    menu: {
        // 创建菜单：menu/create?access_token=ACCESS_TOKEN
        create: `${prefix}menu/create?`
    }
};

// 函数对象
let Wechat = function(opts) {

    this.appID = opts.appID;
    this.appSecret = opts.appSecret;
    // 从本地txt获取
    this.getAccessToken = opts.getAccessToken;
    // 保存到本地txt
    this.saveAccessToken = opts.saveAccessToken;

    this.fetchAccessToken();
};

// 获取合法的access_token
Wechat.prototype.fetchAccessToken = function(name) {

    // 有效
    if(this.access_token && this.expires_in && this.isValidAccessToken(this)) {
        return Promise.resolve(this);
    }

    return new Promise((resolve, reject) => {
        // 从txt读到的access_token
        this.getAccessToken()
        .then(data => {

            try {
                data = JSON.parse(data);
            } catch (error) {
                console.log('error update');
                return this.updateAccessToken();
            }

            // 合法
            if(this.isValidAccessToken(data)) {
                return Promise.resolve(data);
            } else {
                console.log('outdate update');
                // 更新
                return this.updateAccessToken();
            }
        }).then(data => {

            this.access_token = data.access_token;
            this.expires_in = data.expires_in;
            this.saveAccessToken(data);

            // return Promise.resolve(data);
            resolve(data);
        }).catch(data => {
            console.log('getAccessToken：' + data);
            // return Promise.reject(data)
            reject('data');
        });
    });
};

// 判断access_token是否合法
Wechat.prototype.isValidAccessToken = function(data) {

    if(!data || !data.access_token || !data.expires_in) {
        console.log('access_token不合法');
        return false;
    }

    let access_token = data.access_token;
    let expires_in = data.expires_in;
    let now = new Date().getTime();

    if(now < expires_in) {
        console.log('access_token合法')
        return true;
    } else {
        console.log('access_token过期');
        return false;
    }
};

// 更新access_token
Wechat.prototype.updateAccessToken = function() {

    let appID = this.appID;
    let appSecret = this.appSecret;
    let url = `${api.accessToken}&appid=${appID}&secret=${appSecret}`;

    return new Promise((resolve, reject) => {

        // get or post
        request({
            url,
            json: true
        }).then(res => {

            let data = res.body;

            if(data["errcode"]) {
                console.log(data["errmsg"]);
                reject('出错了老铁');
            } else {
                console.log('access_token更新成功：\n' + data.access_token);
                // 过期时间，提前处理
                let now = new Date().getTime();
                let expires_in = now + (data.expires_in - 20) * 1000;

                data.expires_in = expires_in;

                resolve(data);
            }
        });
    });
};

// 新增临时/长久素材
Wechat.prototype.uploadMaterial = function(type, material, permanent) {

    let form = {
        type
    };

    // 新增临时素材
    let uploadUrl = api.upload;

    if(permanent) {
        // 新增一般永久素材
        uploadUrl = api.permanent.upload;
        _.extend(form, permanent);
    } 

    // 永久图文内图片
    if(type === 'pic') {
        uploadUrl = api.permanent.uploadNewsPic;
    }

    // 永久图文
    if(type === 'news') {
        uploadUrl = api.permanent.uploadNews;
        form  = material;
    } else {
        form.media = fs.createReadStream(material)
    }

    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${uploadUrl}&access_token=${data.access_token}`;
            // 除了永久图文，其他素材都需要access_token，不过但加无妨
            form.access_token = data.access_token;

            // 除了永久图文和图文内图片不需要type，其他都需要
            if(type !== 'pic' && type !== 'news') {
                url += '&type=' + type;
            }

            let options = {
                method: 'POST',
                url,
                json: true
            };

            if(type === 'news') {
                // 图文上传body
                options.body = form;
            } else {
                options.formData = form;
            }

            request(options)
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('upload material fails');
                } else {
                    resolve(_data);
                }
            });
        }).catch(_data => {
            reject(_data);
        });
    });
};

// 获取临时/长久素材
Wechat.prototype.fetchMaterial = function(mediaId, type, permanent) {

    let form = {};
    let fetchUrl = api.fetch;

    // 永久素材接口
    if(permanent) {
        fetchUrl = api.permanent.fetch;
    }

    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${fetchUrl}&access_token=${data.access_token}`;
            let method = 'POST';
            let form = {
                media_id: mediaId,
                access_token: data.access_token
            };

            if(!permanent) {
                url += `&media_id=${mediaId}`;
                method = 'GET';
                if(type === 'video') {
                    url = url.replace('https://', 'http://');
                }
            }

            let options = {
                method,
                url,
                body: form,
                json: true
            };

            if(type === 'news' || type === 'video') {
                // 图文和视频素材返回一定的json格式

                require(options)
                .then(res => {
                    let _data = res.body;
                    if(_data['errcode']) {
                        console.log(_data);
                        reject(_data);
                        throw new Error('fetch material fails');
                    } else {
                        resolve(_data);
                    }
                });
            } else {
                // 其他素材直接内容

                resolve(url)
            }

        }).catch(_data => {
            reject(_data);
        });
    });
};

// 删除素材
Wechat.prototype.delMaterial = function(mediaId) {

    let form = {
        media_id: mediaId
    };

    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${api.permanent.del}&access_token=${data.access_token}&media_id=${mediaId}`;

            if(!permanent && type === 'video') {
                url = url.replace('https://', 'http://');
            }

            request({
                method: 'POST',
                url,
                body: form,
                json: true
            })
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('del material fails');
                } else {
                    resolve(_data);
                }
            });
        }).catch(_data => {
            reject(_data);
        });
    });
};

// 修改素材
Wechat.prototype.updateMaterial = function(mediaId, news) {

    let form = {
        media_id: mediaId
    };

    _.extend(form, news);

    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${api.permanent.update}&access_token=${data.access_token}`;

            request({
                method: 'POST',
                url,
                body: form,
                json: true
            })
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('update material fails');
                } else {
                    resolve(_data);
                }
            });
        }).catch(_data => {
            reject(_data);
        });
    });
};

// 素材总数
Wechat.prototype.countMaterial = function() {

    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${api.permanent.count}&access_token=${data.access_token}`;

            request({
                method: 'GET',
                url,
                json: true
            })
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('count material fails');
                } else {
                    resolve(_data);
                }
            });
        }).catch(_data => {
            reject(_data);
        });
    });
};

// 素材列表
Wechat.prototype.batchMaterial = function(type = 'image', offset = 0, count = 2) {

    let options = {
        type, offset, count
    };
    
    return new Promise((resolve, reject) => {

        this.fetchAccessToken()
        .then(data => {
            let url = `${api.permanent.batch}&access_token=${data.access_token}`;

            _.extend(options, { url, method: 'POST', json: true });

            request(options)
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('batch material fails');
                } else {
                    resolve(_data);
                }
            });
        }).catch(_data => {
            reject(_data);
        });
    });
};

// 回复
Wechat.prototype.reply = function() {

    // 回复的内容
    let content = this.body;
    let message = this.weixin;

    // json2xml封装
    let xml = util.tpl(content, message);
    // console.log(xml);
    this.status = 200;
    this.type = 'application/xml';
    this.body = xml;
};

// 创建标签
Wechat.prototype.createTag = function(name) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.create + 'access_token=' + data.access_token;
    
            let form = {
                tag: {
                    name
                }
            };
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('create tag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 获取已创建标签
Wechat.prototype.fetchTags = function() {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.fetch + 'access_token=' + data.access_token;
    
            request({
                method: 'GET',
                url,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('fetch tag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 编辑标签
Wechat.prototype.updateTag = function(id, name) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.update + 'access_token=' + data.access_token;
    
            let form = {
                tag: {
                    id, name
                }
            };
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('update tag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 删除标签
Wechat.prototype.delTag = function(id) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.del + 'access_token=' + data.access_token;
    
            let form = {
                tag: {
                    id
                }
            }
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('del tag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
}

// 获取标签下粉丝列表
Wechat.prototype.getUser = function(id, next_openid) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.getUser + 'access_token=' + data.access_token;
    
            let form = {
                tag: {
                    id, next_openid
                }
            };
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('get tag users fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 批量打标签
Wechat.prototype.batchTag = function(tagid, openid_list) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.update + 'access_token=' + data.access_token;
    
            let form = {
                tagid, openid_list
            };
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('batch tag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 批量取消标签
Wechat.prototype.batchUnTag = function(tagid, openid_list) {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {

            let url = api.tag.update + 'access_token=' + data.access_token;
    
            let form = {
                tagid, openid_list
            };
    
            request({
                method: 'POST',
                url,
                body: form,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('batch untag fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 批量/获取用户信息
Wechat.prototype.fetchUsersInfo = function(openids, lang = 'zh_CN') {
    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {
            let url = '';
            let options = {}

            if(_.isArray(openids)) {
                options.method = 'POST'
                options.body = {
                    user_list: openids
                }
                url = api.user.batchFetch + 'access_token=' + data.access_token;
            } else {
                options.method = 'GET';
                url = api.user.fetch + 'access_token=' + data.access_token + '&openid=' + openids + '&lang=' + lang; 
            }

            options.url = url;
            options.json = true;

            request(options)
            .then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('fetch Users fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 获取用户列表
Wechat.prototype.listUsers = function(openId) {

    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {
            let url = api.user.list + 'access_token=' + data.access_token;
            if(openId) {
                url += '&next_openid=' + openid;
            }

            request({
                method: 'GET',
                url,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('list users fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

// 群发
Wechat.prototype.sendByTag = function(type, message, groupId) {

    let msg = {
        filter: {},
        msgtype: type
    }

    msg[type] = message;

    if(!groupId) {
        msg.filter.is_to_all = true;
    } else {
        msg.filter = {
            is_to_all: false,
            group_id: groupId
        }
    }

    return new Promise((resolve, reject) => {
        this.fetchAccessToken()
        .then(data => {
            let url = api.mass.tag + 'access_token=' + data.access_token;

            request({
                method: 'POST',
                url,
                body: msg,
                json: true
            }).then(res => {
                let _data = res.body;
                if(_data['errcode']) {
                    console.log(_data);
                    reject(_data);
                    throw new Error('list users fails');
                } else {
                    resolve(_data);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
};

module.exports = Wechat;
